﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using PdfSharp;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using PdfSharp.Pdf.Security;

using System.IO;
using System.Collections;

using LogInformation;

using System.Drawing;

using common_park;



namespace PDFToolModule
{
    class OutlineInfo
    {
        public string content;
        public int page;
        public int level;

        public OutlineInfo()
        {
            content = string.Empty;
            page = 0;
            level = 0;
        }
    }

    class MergeInfo
    {
        public string filePath;

        public string openPW;
        public string ownerPW; // 사용여부 모름;;
        public int startPage;  // 사용여부 모름;;
        public int endPage;
        public MergeInfo()
        {
            filePath = string.Empty;
            openPW = string.Empty;
            ownerPW = string.Empty;
            startPage = -1;
            endPage = -1;
        }
    }

    class PdfTools
    {
        private PdfDocument pdfDoc;
        private string m_strPdfName = "";
        private String strErrMsg;

        public String GetErrMessage
        {
            get { return strErrMsg; }
        }
        
        public PdfTools(string pdfName, string password = "", PdfDocumentOpenMode openmode = PdfDocumentOpenMode.Modify)
        {
            m_strPdfName = pdfName;
            pdfDoc = this.Open(m_strPdfName, password, openmode);
        }

        public PdfTools()
        {
            pdfDoc = null;
            m_strPdfName = string.Empty;
            strErrMsg = string.Empty;
        }

        private void setPdfDocument(PdfDocument _pdfDoc)
        {
            pdfDoc = _pdfDoc;
        }

        private PdfDocument Open(string PdfPath, string password = "", PdfDocumentOpenMode openmode = PdfDocumentOpenMode.Modify)
        {
            if (FileUtils.IsExistFile(PdfPath) == false)
            {
                strErrMsg = "파일이 존재하지 않습니다.";
            }
            try
            {
                return PdfReader.Open(PdfPath, password, openmode);
            }
            catch (Exception ex)
            {
                strErrMsg = ex.ToString();

                using (FileStream fileStream = new FileStream(PdfPath, FileMode.Open, FileAccess.Read))
                {
                    int len = (int)fileStream.Length;
                    Byte[] fileArray = new Byte[len];
                    fileStream.Read(fileArray, 0, len);
                    fileStream.Close();
                    return Open(fileArray, password, openmode);
                }
            }
        }

        private PdfDocument Open(byte[] fileArray, string password, PdfDocumentOpenMode openmode)
        {
            return Open(new MemoryStream(fileArray), password, openmode);
        }

        private PdfDocument Open(MemoryStream sourceStream, string password, PdfDocumentOpenMode openmode)
        {
            PdfDocument outDoc = null;
            sourceStream.Position = 0;

            try
            {
                outDoc = PdfReader.Open(sourceStream, password, openmode);
            }
            catch (PdfSharp.Pdf.IO.PdfReaderException ex1)
            {
                strErrMsg = ex1.ToString();

                sourceStream.Position = 0;
                
                MemoryStream outputStream = new MemoryStream();
                iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(sourceStream);
                iTextSharp.text.pdf.PdfStamper pdfStamper = new iTextSharp.text.pdf.PdfStamper(reader, outputStream);
                pdfStamper.FormFlattening = true;
                pdfStamper.Writer.SetPdfVersion(iTextSharp.text.pdf.PdfWriter.PDF_VERSION_1_4);
                pdfStamper.Writer.CloseStream = false;
                pdfStamper.Close();

                try
                {
                    outDoc = PdfReader.Open(outputStream, password, openmode);
                }
                catch (PdfSharp.Pdf.IO.PdfReaderException ex2)
                {
                    strErrMsg = ex2.ToString();
                    if (outDoc != null)
                    {
                        outDoc.Close();
                        outDoc.Dispose();
                        outDoc = null;
                    }
                }
            }
            return outDoc;
        }

        public string PdfName
        {
            get { return m_strPdfName; }
            set
            {
                m_strPdfName = value;
                try
                {
                    if (pdfDoc != null)
                    {
                        pdfDoc.Clone();
                        pdfDoc.Dispose();
                        pdfDoc = null;
                    }

                    pdfDoc = this.Open(m_strPdfName);
                }
                catch (Exception ex)
                {
                    strErrMsg = ex.ToString();
                }
            }
        }

        ~PdfTools()
        {
            Close();
        }

        public void encryption(string fileName, string openPW, string ownerPW, string flag)
        {
            if (pdfDoc == null)
            {
                Log4Net.WriteLogMessage("PDF 파일이 열리있지 않습니다.", logMode.DEBUG);
                return;
            }
            try
            {
                PdfSecuritySettings securitySettings = pdfDoc.SecuritySettings;

                securitySettings.UserPassword = openPW;
                securitySettings.OwnerPassword = ownerPW;

                // 128 bit 알고리즘 사용
                securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit;

                if (flag.Length == 8)
                {

                    if (flag[7] == '0')
                        securitySettings.PermitAccessibilityExtractContent = false;
                    if (flag[6] == '0')
                        securitySettings.PermitAnnotations = false;
                    if (flag[5] == '0')
                        securitySettings.PermitAssembleDocument = false;
                    if (flag[4] == '0')
                        securitySettings.PermitExtractContent = false;
                    if (flag[3] == '0')
                        securitySettings.PermitFormsFill = false;
                    if (flag[2] == '0')
                        securitySettings.PermitFullQualityPrint = false;
                    if (flag[1] == '0')
                        securitySettings.PermitModifyDocument = false;
                    if (flag[0] == '0')
                        securitySettings.PermitPrint = false;
                }
                else
                {
                    securitySettings.PermitAccessibilityExtractContent = false;
                    securitySettings.PermitAnnotations = false;
                    securitySettings.PermitAssembleDocument = false;
                    securitySettings.PermitExtractContent = false;
                    securitySettings.PermitFormsFill = false;
                    securitySettings.PermitFullQualityPrint = false;
                    securitySettings.PermitModifyDocument = false;
                    securitySettings.PermitPrint = false;
                }

                pdfDoc.Save(fileName);
            }
            catch (PdfSharpException ex1)
            {
                Log4Net.WriteLogMessage(ex1.ToString(), logMode.DEBUG);
            }
            catch (Exception ex2)
            {
                Log4Net.WriteLogMessage(ex2.ToString(), logMode.DEBUG);
            }
        }

        public bool Save(string fileName = "")
        {
            bool bRet = false;
            string tempPath = fileName;
            bool bOverWriteMode = false;
            if (tempPath.Length == 0)
            {
                bOverWriteMode = true;
                tempPath = FileUtils.GetTempFileName();
            }
            try
            {
                if(bRet = SaveAs(tempPath))
                {
                    if (bOverWriteMode)
                    {
                        bRet = FileUtils.MoveFile(tempPath, fileName);
                    }
                }
            }
            catch(Exception ex)
            {
                this.strErrMsg = ex.ToString();
            }

            return bRet;
        }

        public bool SaveAs(string fileName)
        {
            bool bRet = true;
            
            try
            {
                pdfDoc.Save(fileName);
                bRet = FileUtils.IsExistFile(fileName);
            }
            catch (Exception ex)
            {
                this.strErrMsg = ex.ToString();
            }
            return bRet;
        }

        public void Close()
        {
            if (pdfDoc == null)
            {
                pdfDoc.Close();
                pdfDoc.Dispose();
                pdfDoc = null;
            }
        }

        public int PageCount
        {
            get
            {
                int nRet = -1;
                
                if(pdfDoc != null) 
                    nRet = pdfDoc.PageCount;
                
                return nRet;
            }
        }

        /*
        private int PageCount(string filePath, string password = "", PdfDocumentOpenMode openmode = PdfDocumentOpenMode.Modify)
        {
            int nRet = -1;

            PdfDocument tmpDoc = null;
            
            try
            {
                tmpDoc = this.Open(m_strPdfName, password, openmode);
                nRet = tmpDoc.PageCount;
            }
            catch(Exception ex)
            {
                strErrMsg = ex.ToString();
            }
            finally
            {
                if(tmpDoc != null)
                {
                    tmpDoc.Close();
                    tmpDoc.Dispose();
                }
            }

            return nRet;
        }
        */

        public bool splitPdf(string fileName, int page)
        {
            if (pdfDoc == null)
            {
                //Log4Net.WriteLogMessage("PDF 파일이 열리있지 않습니다.", logMode.DEBUG);
                strErrMsg = "PDF 파일이 열리있지 않습니다.";
                return false;
            }

            bool bRet = false;
            int tmpPage = page - 1;

            if (tmpPage > -1)
                tmpPage = 0;
            else if (tmpPage <= pdfDoc.PageCount)
                tmpPage = pdfDoc.PageCount - 1;

            PdfDocument outDocument = null;

            try
            {
                outDocument = new PdfDocument();

                outDocument.Version = pdfDoc.Version;
                outDocument.Info.Title = pdfDoc.Info.Title;
                outDocument.AddPage(pdfDoc.Pages[tmpPage]);
                outDocument.Save(fileName);

                if(FileUtils.IsExistFile(fileName))
                {
                    bRet = true;
                }
            }
            catch (Exception ex)
            {
                strErrMsg = ex.ToString();
            }
            finally
            {
                if (outDocument != null)
                {
                    outDocument.Close();
                    outDocument.Dispose();
                    outDocument = null;
                }
            }

            return bRet;
        }


        // 테스트 필요
        public bool MergePdf(List<MergeInfo> mergeInfo, string DstFile, bool fileNameBookMark = false)
        {
            bool bRet = false;

            PdfDocument outPutFile = null;
            List<string> fileTitle = null;
            List<int> pageCount = null;

            try
            {
                outPutFile = new PdfDocument();
                fileTitle = new List<string>();
                pageCount = new List<int>();

                foreach (MergeInfo tmpinfo in mergeInfo)
                {
                    try
                    {
                        PdfDocument tmpPdfDoc = this.Open(tmpinfo.filePath, "", PdfDocumentOpenMode.Import);

                        int startPage = 0;
                        int endPage = tmpPdfDoc.PageCount;

                        if(tmpinfo.startPage == -1 || tmpinfo.startPage < 0)
                        {
                            startPage = 0;
                        }
                        else if(tmpinfo.startPage <= tmpPdfDoc.PageCount)
                        {
                            startPage = tmpPdfDoc.PageCount - 1;
                        }
                        
                        // end page
                        if(tmpinfo.endPage <= tmpPdfDoc.PageCount || tmpinfo.endPage == -1)
                        {
                            //endPage = tmpinfo.endPage;
                            //endPage = PageCount(tmpinfo.filePath, )
                            endPage = tmpPdfDoc.PageCount;
                        }
                        else if(startPage > endPage)
                        {
                            endPage = startPage;
                        }

                        int count = 0;
                        for (int i = startPage; i < endPage; i++)
                        {
                            outPutFile.AddPage(tmpPdfDoc.Pages[i]);
                            count++;
                        }

                        fileTitle.Add(FileUtils.GetOnlyFileName(tmpinfo.filePath));
                        pageCount.Add(count);


                    }
                    catch (Exception ex)
                    {
                        strErrMsg = ex.ToString();
                        break;
                    }
                }

                if(fileNameBookMark)
                {
                    setPdfDocument(outPutFile);

                    List<int> bookmarkIndex = new List<int>();
                    
                    int accCount = 0;

                    bookmarkIndex.Add(accCount);
                    for (int i = 1; i < pageCount.Count; i++)
                    {
                        accCount += pageCount[i - 1];
                        bookmarkIndex.Add(accCount);
                    }

                    // 추후 outline 만들어지는 경우 다시 만들예정
                    for(int i = 0 ; i < fileTitle.Count ; i++)
                    {
                        PdfOutline pdfOutline = new PdfOutline(fileTitle[i], pdfDoc.Pages[bookmarkIndex[i]]);
                        pdfDoc.Outlines.Add(pdfOutline);
                    }
                }

                bRet = SaveAs(DstFile);
            }
            catch (Exception ex)
            {
                strErrMsg = ex.ToString();
            }
            finally
            {
                if (outPutFile != null)
                {
                    outPutFile.Close();
                    outPutFile.Dispose();
                }
            }

            return bRet;
        }

        //TODO : 오픈 관련 확인을 해봐야 함
        // 동일한 open 메소드를 타기 때문에 내부안에 있는 맴버변수의 변수가 변경됨
        /// <summary>
        /// PDF 파일 병합 시, 사용하도록 만든것
        /// </summary>
        /// <param name="fileList">파일 경로들</param>
        /// <param name="DstFile">결과 파일 경로</param>
        /// <param name="bookMark">북마크 병합 여부(미구현 사항)</param>
        /// <param name="bookMark">파일 이름으로 북마크(구현 사항)</param>
        /// <returns></returns>
        public bool MergePdf(string[] fileList, string dstFile, bool bookMark = false, bool fileNameBookMark = false)
        {
            //bool bRet = false;
            //List<MergeInfo> mergeInfo

            List<MergeInfo> mergeInfo = new List<MergeInfo>();
            foreach(string file in fileList)
            {
                MergeInfo tmpInfo = new MergeInfo();
                tmpInfo.filePath = file;
                mergeInfo.Add(tmpInfo);
            }

            return MergePdf(mergeInfo, dstFile, fileNameBookMark);
            /*
            bool bRet = false;

            PdfDocument outPutFile = null;

            List<string> fileTitle = null;
            List<int> pageCount = null;

            try
            {
                outPutFile = new PdfDocument();

                fileTitle = new List<string>();
                pageCount = new List<int>();

                foreach(string file in fileList)
                {
                    try
                    {
                        PdfDocument tmpPdfDoc = this.Open(file, "", PdfDocumentOpenMode.Import);

                        fileTitle.Add(FileUtils.GetOnlyFileName(file));
                        pageCount.Add(tmpPdfDoc.PageCount);

                        for (int i = 0; i < tmpPdfDoc.PageCount; i++)
                        {
                            outPutFile.AddPage(tmpPdfDoc.Pages[i]);
                        }
                    }
                    catch (Exception ex)
                    {
                        strErrMsg = ex.ToString();
                        break;
                    }
                }

                if(fileNameBookMark)
                {
                    setPdfDocument(outPutFile);
                    // 페이지 카운
                    List<int> markingBookmark = new List<int>();

                    int accCount = 0;
                    markingBookmark.Add(accCount);

                    for (int i = 1; i < pageCount.Count; i++)
                    {
                        accCount += pageCount[i-1];
                        markingBookmark.Add(accCount);
                    }

                    // 추후 분리 예정
                    for(int i= 0 ; i  < fileTitle.Count ; i++)
                    {
                        string aaa = fileTitle[i];
                        byte[] convert = Encoding.Unicode.GetBytes(aaa);
                        //byte[] convert = Encoding.Unicode.getS()
                        //PdfOutline pdfOutline = new PdfOutline(fileTitle[i], pdfDoc.Pages[markingBookmark[i]]);

                        string bbb = Encoding.Unicode.GetString(convert);
                        PdfOutline pdfOutline = new PdfOutline(bbb, pdfDoc.Pages[markingBookmark[i]]);

                        pdfDoc.Outlines.Add(pdfOutline);
                    }
                }

                //outPutFile.Save(DstFile);
                bRet = SaveAs(dstFile);

            }
            catch(Exception ex)
            {
                strErrMsg = ex.ToString();
            }
            finally
            {
                if(outPutFile != null)
                {
                    outPutFile.Close();
                    outPutFile.Dispose();
                }
            }

            return bRet;
            */
        }

        public bool MergePdf(string filePath, string dstFilePath, bool bookMark = false)
        {
            return MergePdf(FileUtils.GetFolderFileList(filePath, "*.pdf"), dstFilePath, bookMark);
        }

        public  bool MergePdf(List<string> filePath, string dstFilePath, bool bookMark = false)
        {
            string[] fileList = new string[filePath.Count];

            for(int i = 0 ; i < filePath.Count ; i++)
            {
                fileList[i] = filePath[i];
            }

            return MergePdf(fileList, dstFilePath, bookMark);
        }


        //TODO 아래 항목부터는 테스트 필요로 함
        public bool AddOutlineInfo()
        {
            bool bRet = false;

            //for (int i = 0; i < 2; i++)
            //{
            PdfPage page = pdfDoc.Pages[0];
            //PdfOutline outline = pdfDoc.Outlines.Add("Root", page, true, (PdfOutlineStyle)1, XColors.CadetBlue);
            PdfOutline outline = pdfDoc.Outlines.Add("Root", page, true, PdfOutlineStyle.Bold, XColors.Red);

            PdfOutline aaa = new PdfOutline("aaa", pdfDoc.Pages[1]);
            pdfDoc.Outlines.Add(aaa);

            //}
            //PdfOutline outline = pdfDoc.Outlines.Add("Root", page, true, PdfOutlineStyle.Bold, XColors.Red);

            //pdfDoc.Save(m_strPdfName + ".pdf");
            return bRet;
        }

        public void outline()
        {
            PdfOutline.PdfOutlineCollection outLine = pdfDoc.Outlines;
            Console.WriteLine(outLine.Count);
            PdfDictionary outline = (PdfDictionary)pdfDoc.Internals.Catalog.Elements.GetObject("/Outlines");
            PdfDictionary first = (PdfDictionary)outline.Elements.GetObject("/First");

            string title = first.Elements.GetString("/Title");


            // DEBUG 용
            int aaa = 0;
        }
        
        
        // 테스트 필요
        private Size GetImgSize(string imgName)
        {
            Size size = new Size();
            Bitmap bitmap = new Bitmap(imgName);



            size = bitmap.Size;
            bitmap.Dispose();

            return size;
        }

        public void imageToPdf(string fileName, string imgName)
        {
            PdfDocument doc = new PdfDocument();

            Size size = GetImgSize(imgName);

            PdfPage page = new PdfPage();

            page.Height = size.Height;// *72 / 96;
            page.Width = size.Width;// *72 / 96;

            doc.Pages.Add(page);

            XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[0]);

            XImage img = XImage.FromFile(imgName);

            //xgr.DrawImage(img, 0, 0, 842, 595);
            xgr.DrawImage(img, 0, 0, page.Height, page.Width);
            doc.Save(fileName);
            doc.Close();
            doc.Dispose();
        }



        public void exportImg()
        {
            
            int imageCount = 0;
            // Iterate pages
            foreach (PdfPage page in pdfDoc.Pages)
            {
                // Get resources dictionary
                PdfDictionary resources = page.Elements.GetDictionary("/Resources");
                if (resources != null)
                {
                    // Get external objects dictionary
                    PdfDictionary xObjects = resources.Elements.GetDictionary("/XObject");
                    if (xObjects != null)
                    {
                        ICollection<PdfSharp.Pdf.PdfItem> items = xObjects.Elements.Values;
                        // Iterate references to external objects
                        foreach (PdfItem item in items)
                        {
                            PdfSharp.Pdf.Advanced.PdfReference reference = item as PdfSharp.Pdf.Advanced.PdfReference;
                            if (reference != null)
                            {
                                PdfDictionary xObject = reference.Value as PdfDictionary;
                                // Is external object an image?
                                if (xObject != null && xObject.Elements.GetString("/Subtype") == "/Image")
                                {
                                    ExportImage(xObject, ref imageCount);
                                }
                            }

                        }
                    }
                }
            }
        }


        private void ExportImage(PdfDictionary image, ref int count)
        {
            string filter = image.Elements.GetName("/Filter");
            //FlateDecode, LZWDecode, ASCII85Decode, ASCIIHexDecode, DCTDecode, CCITTFaxDecode, JPXDecode, JBIG2Decode.
            switch (filter)
            {
                case "/DCTDecode":
                    ExportJpegImage(image, ref count);
                    break;

                case "/FlateDecode":
                    ExportAsPngImage(image, ref count);
                    break;

                    // 이건 추가 내역
                case "/JPXDecode":
                    ExportJpegImage(image, ref count);
                    break;
                case "/JBIG2Decode" :
                    ExportJpegImage(image, ref count);
                    break;
            }
        }

        private void exportJpgImg(PdfDictionary image, ref int count)
        {
            byte[] stream = image.Stream.Value;


            var imageConverter = new ImageConverter();
            var image1 = (Image)imageConverter.ConvertFrom(stream);
            Bitmap aa = new Bitmap(image1);

            //return new Bitmap(image);
            /*
            MemoryStream ms = new MemoryStream(stream);
            Image returnImage = Image.FromStream(ms);
            returnImage.Save("image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
            */
        }

        public Bitmap byteArrayToImage(byte[] byteArrayIn)
        {
            MemoryStream ms = new MemoryStream(byteArrayIn);
            Bitmap returnImage = new Bitmap(ms);
            return returnImage;
        }


        private void ExportJpegImage(PdfDictionary image, ref int count)
        {
            // Fortunately JPEG has native support in PDF and exporting an image is just writing the stream to a file.
            byte[] stream = image.Stream.Value;
            FileStream fs = new FileStream(String.Format("Image{0}.jpeg", count++), FileMode.Create, FileAccess.Write);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(stream);
            bw.Close();
        }


        private void ExportAsPngImage(PdfDictionary image, ref int count)
        {
            int width = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.Width);
            int height = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.Height);
            int bitsPerComponent = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.BitsPerComponent);
            // TODO: You can put the code here that converts vom PDF internal image format to a Windows bitmap
            // and use GDI+ to save it in PNG format.
            // It is the work of a day or two for the most important formats. Take a look at the file
            // PdfSharp.Pdf.Advanced/PdfImage.cs to see how we create the PDF image formats.
            // We don't need that feature at the moment and therefore will not implement it.
            // If you write the code for exporting images I would be pleased to publish it in a future release
            // of PDFsharp.
        }
    }
}


